home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / scheme / schmdtrp / scheme_d.lha / LISP.E < prev    next >
Encoding:
Text File  |  1993-07-16  |  20.8 KB  |  759 lines

  1. /************************************************************************
  2.  * LISP mode for Epsilon --             Steve Ward's variant   3/90    *
  3.  *                                    *
  4.  * Ancestory:                                *
  5.  * .SCM file suffix added 1991 Aubrey Jaffer.                *
  6.  * Portions Copyright (C) 1985 Robert C. Pettengill            *
  7.  *   "Permission is granted to reproduce and use this copyrighted    *
  8.  *    material for any purpose whatsoever."                *
  9.  * Modified by BKPH at MIT; extensive modifications by SAW        *
  10.  *                                    *
  11.  * Additional evolution is encouraged; updates appreciated (email to    *
  12.  *    ward@mit.edu)                            *
  13.  *                                    *
  14.  *                                    *
  15.  ************************************************************************
  16.  *                                    *
  17.  * LISP MODE: Auto-entered on editing .LSP, .S, .SCM (Scheme) files.    *
  18.  *  ) } ]  Shows (blinks) matching delimiter.                *
  19.  *  TAB    Indents current line.  Multiple consecutive tabs typed drop    *
  20.  *        back to standard tab stops, spaced at lisp-tab-size.    *
  21.  *  ENTER  Starts new line at our allegedly correct indent        *
  22.  *                                    *
  23.  *  A-a    Moves to start of current definition (looks for "(" in col 0)*
  24.  *  A-e    Moves past end of current definition                *
  25.  *  A-g    Selects (point, mark) current definition            *
  26.  *  A-tab  Reformats current top-level definition (indents, not fills)    *
  27.  *       With arg (^U A-tab) reformats current subexpression.        *
  28.  *       REPEATED uses: cycle thru selected lisp_indent_modes,    *
  29.  *        reformating same expr/subexpr in various ways; a good way    *
  30.  *        to explore different indentation formats.            *
  31.  *  A-b       Backward S-Expression                    *
  32.  *  A-;       Comment: moves to comment column, adds ;            *
  33.  *  C-A-u  Up a level: moves left to start of current (...) form    *
  34.  *  C-A-d  Down a level: moves right past next (            *
  35.  *                                    *
  36.  * Should be done, but haven't been:                    *
  37.  *  A-F       Forward S-Expression.  Needs forward_thing(), analogous to    *
  38.  *       backward_thing() q.v.  Actually it should be simpler.    *
  39.  *  A-DEL  Backward kill S-expression                    *
  40.  *                                    *
  41.  * Relics of previous incarnations, largely superceded by above:    *
  42.  *   C-A-F    forward-level (use A-F?)                *
  43.  *   C-A-B    backward-level (use A-B?)                *
  44.  *   C-A-K    kill-level: Kills next (...) form            *
  45.  *   A-<del>  backward-kill-level                    *
  46.  *                                    *
  47.  ************************************************************************
  48.  *                                    *
  49.  * WARNINGS:                                *
  50.  *                                    *
  51.  *  (1) Much of what follows is based on sloppy heuristics, rather    *
  52.  *    than precise algorithms.  This includes details such as     *
  53.  *    handling of comments - unbalanced parens in comments, eg,    *
  54.  *    will fool this code.  Likewise, it is naive about strings, etc    *
  55.  *                                    *
  56.  *    It is perfectly possible to write C code to take these factors    *
  57.  *    properly into account; unfortunately, that involves coding the    *
  58.  *    low-level char-by-char scanning in EEL rather than using    *
  59.  *    searches and is intolerably slow.  If you'd like to play with    *
  60.  *    this approach, the key routine to make smarter is        *
  61.  *    lisp_backward_thing(), qv.                    *
  62.  *                                    *
  63.  *  (2) Top-level forms are presumed to be exactly those which begin    *
  64.  *    in column 0 (as opposed to those starting with "(def", etc).    *
  65.  *                                    *
  66.  *  (3) My indentation heuristic tries to align each line with the     *
  67.  *    start of the previous S-expression (ignoring comments), except    *
  68.  *    in the special case where the line containing the start of that *
  69.  *    S-expression begins a top-level    form (detected by a left paren    *
  70.  *    in column 0).  In the latter case, the line is just inset by    *
  71.  *    one tab stop.                            *
  72.  *                                    *
  73.  ************************************************************************
  74.  *                                    *
  75.  * User-hackable variables:                        *
  76.  *                                    *
  77.  *  int lisp-comment-col  (default=40)                    *
  78.  *                                    *
  79.  *  int lisp-tab-size: tab stop interval (default=2)            *
  80.  *                                    *
  81.  *  int lisp_tab_mode: controls action on TAB key. Interesting values:    *
  82.  *    0:    conventional tabs, like text mode.            *
  83.  *    1226:    A=2, X=1, Y=3, Z=2.  The default.  Behavior is:        *
  84.  *       First tab tries to use heuristic indent; from there on,     *
  85.  *       additional tabs afford primitive control over formatting.    *
  86.  *                                    *
  87.  *    Word is parsed as follows: A + 8*x + 64*Y + 512*Z, where    *
  88.  *       A: 2-bit field controlling action when tab typed outside    *
  89.  *          of indententation (left-hand white space):        *
  90.  *        0: raw tabs: inserts tab chars into buffer.        *
  91.  *        1: conventional tabs, set at lisp_tab_size intervals.    *
  92.  *        2: not recognized as special case (handled as below)    *
  93.  *       X: Field controlling action on 1st tab typed            *
  94.  *       Y: Field controlling action on 2nd tab typed            *
  95.  *       Z: Field controlling action on 3rd & successive tabs typed    *
  96.  *    Each 3-bit field can have the following values:            *
  97.  *        0: raw tabs: inserts tab chars into buffer.        *
  98.  *        1: set indent according to heuristic            *
  99.  *        2: increase indent by one lisp_tab_size            *
  100.  *        3: set indent to 1st tab stop (column lisp_tab_size)    *
  101.  *        4: set indent to left margin                *
  102.  *        5: conventional tabs, set at lisp_tab_size intervals.    *
  103.  *                                    *
  104.  *  int lisp_indent_mode: controls choice of indenting heuristic.    *
  105.  *        Microcoded; turning bits OFF adds hacks.            *
  106.  *    0:  No indent; always assumes line 0.                *
  107.  *    1:  Always same as previous line.                *
  108.  *     16:  My heuristic (as explained above), wide-mode.  Aligns with    *
  109.  *         last form at same level.                    *
  110.  *   16+1:  My heuristic, without the top-level-detect hack.        *
  111.  *   16+2:  My heuristic, narrow-mode.  Tries to align with the left-    *
  112.  *         most previous form on the same level.            *
  113.  *   16+4:  Avoid going beyond tab stop after one at which containing    *
  114.  *        form starts. BKPH's hack of indenting 1 if the car of that    *
  115.  *        containing form is compound, else tabbing, is maintained.    *
  116.  *   16+8:  My heuristic, compromise-mode.  Aligns with 2nd form at    *
  117.  *         current level, rather than left-most.            *
  118.  *                                    *
  119.  *                                    *
  120.  ************************************************************************/
  121.  
  122. /************************************************************************
  123.  *                                    *
  124.  * Following are samples of indenting modes:                *
  125.  *                                    *
  126.  ************************************************************************
  127.  
  128.  
  129. ; mode=23:
  130. (define (foo bar)
  131.     (cond ((eq foo bar)
  132.            (prog nil
  133.              (setq bar foo)
  134.              (setq foo bar)))
  135.           ((atom foo)((bar foo) bar))
  136.           (t (foo (foo (foo (foo bar bar)
  137.                 (bar bar foo) ))))))
  138.  
  139. ; mode=22:
  140. (define (foo bar)
  141.   (cond ((eq foo bar)
  142.      (prog nil
  143.            (setq bar foo)
  144.            (setq foo bar)))
  145.     ((atom foo)((bar foo) bar))
  146.     (t (foo (foo (foo (foo bar bar)
  147.               (bar bar foo) ))))))
  148.  
  149.  
  150.  
  151. ; mode=20: "Steve mode"
  152. (define (foo bar)
  153.   (cond ((eq foo bar)
  154.      (prog nil
  155.            (setq bar foo)
  156.            (setq foo bar)))
  157.     ((atom foo)((bar foo) bar))
  158.     (t (foo (foo (foo (foo bar bar)
  159.               (bar bar foo) ))))))
  160.  
  161. ; mode=20 again, typed differently:
  162. (define (foo bar)
  163.   (cond
  164.    ((eq foo bar)
  165.     (prog nil
  166.       (setq bar foo)
  167.       (setq foo bar)))
  168.    ((atom foo)((bar foo) bar))
  169.    (t (foo (foo (foo (foo bar bar)
  170.              (bar bar foo) ))))))
  171.  
  172.  
  173. ; mode=19: pure "BKPH mode"
  174. (define (foo bar)
  175.   (cond ((eq foo bar)
  176.      (prog nil
  177.        (setq bar foo)
  178.        (setq foo bar)))
  179.     ((atom foo)((bar foo) bar))
  180.     (t (foo (foo (foo (foo bar bar)
  181.            (bar bar foo) ))))))
  182.  
  183.  
  184. ; mode=28, super-compact mode
  185. (define (foo bar)
  186.   (cond
  187.    ((eq foo bar)
  188.     (prog nil
  189.      (setq bar foo)
  190.      (setq foo bar)))
  191.    ((atom foo)((bar foo) bar))
  192.    (t (foo (foo (foo (foo bar bar)
  193.          (bar bar foo) ))))))
  194.  
  195.  
  196.  ************************************************************************
  197.  *                                    *
  198.  * End of indenting mode samples.                    *
  199.  *                                    *
  200.  ************************************************************************/
  201.  
  202.  
  203.  
  204.  
  205. #include "eel.h"
  206.  
  207.  
  208. /************************************************************************
  209.  *                                    *
  210.  * User-hackable variables:                        *
  211.  *                                    *
  212.  ************************************************************************/
  213.  
  214. int lisp_comment_col = 40;    /* column for lisp comments to begin     */
  215. int lisp_tab_size = 2;        /* spacing between tab stops        */
  216.  
  217. int lisp_indent_mode = 2;    /* Indentation heuristic        */
  218.  
  219. int lisp_tab_mode =         2    /* Field A: tab outside of indentation    */
  220.           +(  8*    1)    /* Field X: first tab typed        */
  221.           +( 64*    3)    /* Field Y: 2nd tab typed        */
  222.           +(512*    2);    /* Field Z: successive tabs typed.    */
  223.  
  224.  
  225. /************************************************************************
  226.  *                                    *
  227.  * Internals:                                *
  228.  *                                    *
  229.  ************************************************************************/
  230.  
  231. keytable lisp_tab;        /* key table for lisp mode         */
  232.  
  233. #define LEFTD    '('        /* Delimiters. NB: CHARACTERS, not    */
  234. #define RIGHTD    ')'        /*   STRINGS (as elsewhere in EEL)    */
  235.  
  236. /* These values are chosen pretty randomly... somebody should check to
  237.  * see if theres a systematic way to allocate them:
  238.  */
  239. #define LISP_TAB_1    69       /* this_cmd code for first tab command     */
  240. #define LISP_TAB_2    70       /* this_cmd code for 2nd+ tab command     */
  241. #define LISP_REFORMAT_TOP  71    /* Reformat top-level expression.    */
  242. #define LISP_REFORMAT_SUB  72    /* Reforamt subexpression.        */
  243.  
  244.  
  245.  
  246. /* Main indentation heuristic:
  247.  *
  248.  * lisp_compute_indent() returns the column (0-indexed) that it thinks
  249.  *   the current line should begin at.  No side-effects; point unchanged.
  250.  * This is the first place to hack if you don't like the current format.
  251.  *
  252.  * Current heuristic:
  253.  *   (1) Aligns with start of last "thing" (approximately, S-expr) on
  254.  *    previous line; EXCEPT
  255.  *   (2) If previous line starts in column 0, starts one lisp-tab-size
  256.  *    indented. Here "previous line" means last line with something
  257.  *    on it... we try to skip comments.
  258.  *
  259.  * This latter clause conforms to the common practice of writing
  260.  *   (DEFUN FUNGUS(X Y)
  261.  *     (COND ... 
  262.  * rather than the less compact
  263.  *   (DEFUN FUNGUS(X Y)
  264.  *                (COND ... 
  265.  */
  266.  
  267.  
  268. lisp_compute_indent()
  269.  {    int begline, backone, prevcol, leftcol, col, s, i;
  270.     int orig = point;
  271.  
  272.     if (lisp_indent_mode == 0) return 0;
  273.  
  274.     to_begin_line();    /* Go to start of this line.        */
  275.     begline = point;
  276.  
  277.     /* If the line starts in column 0, presume indent of 0:        */
  278.     if (character(point) > ' ') return 0;
  279.  
  280.     if (point == 0) { point=orig; return 0; }
  281.  
  282.     if (lisp_indent_mode == 1)
  283.      { --point;
  284.        to_indentation();
  285.        leftcol = current_column();
  286.        goto cur;
  287.      }
  288.  
  289.     /* We're left with some variant of my heuristic.  If others are
  290.      * added, we should dispatch (eg) on lisp_indent>mode >> 5.
  291.      */
  292.  
  293.     s = backward_thing();    /* Move back one S-expr            */
  294.     backone = point;
  295.     /* If we've moved to column 0, or if we were blocked (meaning
  296.      * beginning of buffer), then indent is 0.
  297.      */
  298.     leftcol = 0;
  299.     if ((s == 0) ||
  300.         ((s == 1) && (current_column() == 0))) goto cur;
  301.  
  302.     if ((prevcol=current_column()) == 0) goto cur;
  303.  
  304.     /* Now we're presumably on previous line (more precisely, on
  305.      * the line at which the previous "thing" begins).  See if this
  306.      * is the line which starts a definition (ie, in column 1):    */
  307.  
  308.     if ((lisp_indent_mode&1) == 0)
  309.      { to_begin_line();    /* Start of previous line.        */
  310.        if (character(point) == '(')
  311.         { point=orig;
  312.           return lisp_tab_size;
  313.         }
  314.      }
  315.  
  316.     /* OK, use the general case: start of previous expression.    */
  317.     point = backone;
  318.  
  319.     leftcol = prevcol;
  320.  
  321.     if ((lisp_indent_mode&2) == 0)
  322.      { /* Narrow-mode hack - try to go back a thing, finding the
  323.         * first of several on the line:
  324.         */
  325.        i = -1;
  326.        while (backward_thing() == 1)
  327.         { if ((col=current_column()) >= leftcol) break;
  328.           i = leftcol;
  329.           leftcol = col;
  330.         }
  331.        if (((lisp_indent_mode&8) == 0) && (i > 0)) leftcol = i;
  332.      }
  333.  
  334.  
  335.     if ((lisp_indent_mode&4) == 0)
  336.      { point = backone;
  337.  
  338.        if (s == 2) point = backone;
  339.        else
  340.         { point = orig;
  341.           if (lisp_move_level(-1, RIGHTD, LEFTD) <= 0) goto cur;
  342.         }
  343.           
  344.        col = current_column();
  345.        if (character(point+1) == '(') leftcol = col+1;
  346.        else leftcol = col+lisp_tab_size;
  347.      }
  348.  
  349. cur:    point = orig;
  350.     return leftcol;
  351.  }
  352.  
  353. /* Move to the beginning of current word....
  354.  * added since I couldn't get backward_word to do what I wanted, for
  355.  *   unknown reasons.
  356.  */
  357. lisp_word_beginning()
  358.  {    char ch;
  359.     while ((point>0) && (character(point) <= ' ')) --point;
  360.     while (point>0)
  361.      { --point;
  362.        ch = character(point);
  363.        switch(ch)
  364.         { case '(': case ')':
  365.           case ' ': case '\t': case '\n':
  366.         ++point;
  367.         return 1;
  368.           default: continue;
  369.         }
  370.      }
  371.     return 0;
  372.  }
  373.  
  374. /* We should have real move-expression primitives, which know about
  375.  * comment syntax etc.  Unfortunately, we're reduced to ersatz
  376.  * heuristics...
  377.  * Returns 0 if blocked, 1 on success, 2 if it pops out of a (...)
  378.  */
  379. backward_thing()
  380.  {    char ch;
  381.     int posn, orig, result;
  382.  
  383. again:    orig = point;
  384.     result = 0;
  385.     for (;;)
  386.      { if (!point) return 0;
  387.        --point;
  388.        switch (ch = character(point))
  389.         { case ' ':
  390.           case '\n':
  391.           case '\t':    continue;
  392.           case ')':        ++point;
  393.                 if (move_level(-1, ")", "(")) result=1;
  394.                 goto gotit;
  395.           case '(':        return 2;
  396.           default:        ++point;
  397.                 lisp_word_beginning();
  398.                 if (character(point) == '(') result=2;
  399.                 else result=1;
  400.                 goto gotit;
  401.         }
  402.      }
  403.  
  404.  
  405. gotit:
  406.     /* Now, the problem is that we might have found a word inside
  407.      * of a comment.  Check for this, and punt if necessary
  408.      */
  409.  
  410.     posn = point;        /* Where we've moved to            */
  411.     to_begin_line();    /* Go to start of new line.        */
  412.     if (search(1, ";"))
  413.      {
  414.        if ((point < posn) && (point < orig) &&
  415.            (character(point-1) == ';'))
  416.         {
  417.           --point; --point;
  418.           goto again;
  419.         }
  420.      }
  421.     point = posn;
  422.     return result;
  423.  }
  424.  
  425.  
  426. /* Compute column of next tab stop, assuming we're in column n:
  427.  */
  428. lisp_tab_after(n)
  429.  {    return lisp_tab_size * (1+(n/lisp_tab_size));
  430.  }
  431.  
  432. /* Move in direction dir to find a parenthesis that would
  433.  * match first at point. Return 1 on success.  Otherwise go to
  434.  *  starting point, and return 0.
  435.  */
  436.  
  437. lisp_move_level(dir, first, second)
  438.  char first, second;
  439.  {    int orig = point;
  440.     int level = -dir;        /* hack for up & down level */
  441.     char pat[6];            /* temporary pattern */
  442.  
  443.     sprintf(pat, "[%c%c]", first, second);
  444.     while (re_search(dir, pat))    /* look for either first or second */
  445.      { if (character(point - (dir > 0)) == first) level++;
  446.        else level--;
  447.        if (level == 0) return 1;    /* when we return to same level,done*/
  448.      }
  449.  
  450.     point = orig;
  451.     return 0;
  452.  }
  453.  
  454. /* Find start of current top-level form, using the heuristic that such
  455.  * forms conventionally start with a left paren in column 0.
  456.  */
  457. lisp_find_top_open()
  458.  {    int orig = point;
  459.  
  460.     while (point > 0)
  461.      { to_begin_line();
  462.        if (character(point) == '(') return 1;
  463.        if (point > 0) --point;
  464.      }
  465.     say("No ( at left margin!");
  466.     point = orig;
  467.     return 0;
  468.  }
  469.  
  470.  
  471. /************************************************************************
  472.  *                                    *
  473.  * Lisp mode commands:                            *
  474.  *                                    *
  475.  ************************************************************************/
  476.  
  477. /* Set point to the start of the top-level form, and mark to its end.
  478.  * Returns 1 iff successful, else 0.
  479.  */
  480.  
  481. command lisp_select_top_form() on lisp_tab[ALT('g')]
  482.  {    int start, end;
  483.     if (!lisp_find_top_open()) return 0;
  484.     start = point;
  485.     if (!move_level(1, "(", ")")) return 0;
  486.     end = point;
  487.     mark = end;
  488.     point = start;
  489.     return 1;
  490.  }
  491.  
  492. command lisp_top_beginning() on lisp_tab[ALT('a')]
  493.  {    if (!lisp_find_top_open()) return 0;
  494.     return 1;
  495.  }
  496.  
  497. command lisp_top_end() on lisp_tab[ALT('e')]
  498.  {    if (lisp_select_top_form()) point = mark;
  499.  }
  500.  
  501.  
  502.  
  503. /* re-format current definition:
  504.  * with arg, reformats current expression.
  505.  * Repeated uses change lisp_indent_mode before reformatting.
  506.  */
  507.  
  508. command lisp_reformat() on lisp_tab[ALT('\t')]
  509.  {    int *start = alloc_spot(), *end = alloc_spot(),
  510.         *orig=alloc_spot(), rtn=0;
  511.     char buf[30];
  512.     int  i, j;
  513.  
  514.     *orig = point;
  515.  
  516.     /* On repeated uses, select new indentation mode:        */
  517.     if ((prev_cmd == LISP_REFORMAT_TOP) ||
  518.         (prev_cmd == LISP_REFORMAT_SUB)) lisp_choose_new_mode();
  519.  
  520.     /* Should we reformat current sub-expression?            */
  521.     if ((iter != 1) || (prev_cmd == LISP_REFORMAT_SUB))
  522.      { if (character(point) != '(')
  523.         { while (backward_thing() == 1);
  524.         }
  525.        *start = point;
  526.        if (!move_level(1, "(", ")")) goto err;
  527.        *end = point;
  528.        iter = 1;
  529.        this_cmd = LISP_REFORMAT_SUB;
  530.      }
  531.  
  532.     else            /* Nope, reformat entire top-level sexp    */
  533.      { if (!lisp_select_top_form()) goto err;
  534.        *start = point;
  535.        *end = mark;
  536.        this_cmd = LISP_REFORMAT_TOP;
  537.      }
  538.  
  539.     
  540.     /* Let user know we're working on it...                */
  541.     point = *start;
  542.     for (i=0, j=0; j<29; i++)
  543.      { buf[j] = character(point+i);
  544.        switch (buf[j++])
  545.          { case '\n': --j; goto gotit;
  546.            case '\t': buf[j-1] = ' '; continue;
  547.          }
  548.      }
  549.     gotit: buf[j] = 0;
  550.     say("Re-formatting %s... using lisp_indent_mode = %d",
  551.         buf, lisp_indent_mode);
  552.  
  553.     /* Loop through each line, reformatting it:            */
  554.     while (point < *end)
  555.      { lisp_indenter();
  556.        nl_forward();
  557.      }
  558.  
  559. err:    point = *start;
  560.  
  561.     /* Correct apparent bug: spot seems to drift into white space
  562.      * to left, during reformatting:
  563.      */
  564.     for (;;)
  565.      { i = character(point);
  566.        if ((i != ' ') && (i != '\t')) break;
  567.        ++point;
  568.      }
  569.  
  570.     mark = *end;
  571.  
  572.     say("%s... reformatted in lisp_indent_mode %d",
  573.         buf, lisp_indent_mode);
  574.     free_spot(orig);
  575.     free_spot(start);
  576.     free_spot(end);
  577.     return rtn;
  578.  }
  579.  
  580. /* Cycle thru interesting indentation modes, re-formatting the current
  581.  * definition each time.
  582.  */
  583.  
  584. int lisp_indent_modes[] =
  585.  { 28, 19, 20, 22, 23, 0
  586.  };
  587.  
  588. lisp_choose_new_mode()
  589.  {    int i;
  590.     for (i=0; lisp_indent_modes[i]; i++)
  591.      { if (lisp_indent_mode == lisp_indent_modes[i])
  592.         { lisp_indent_mode = lisp_indent_modes[i+1];
  593.           goto gotmode;
  594.         }
  595.      }
  596.     lisp_indent_mode = 0;
  597. gotmode:
  598.     if (lisp_indent_mode == 0) lisp_indent_mode = lisp_indent_modes[0];
  599.  }
  600.     
  601. command lisp_indenter()        
  602.  {
  603.     to_indentation();        /* this seems to be needed, why? */
  604.     to_column(lisp_compute_indent());
  605.  }
  606.  
  607. /*  Indent an existing line of lisp code.  
  608.  *  If our new indentation matches the old, just insert a tab.
  609.  *  if we're not in this line's indentation indent the line,
  610.  *  but keep point where it was -- bkph.
  611.  */
  612.  
  613. command lisp_indent() on lisp_tab['\t']
  614.  {    int orig = point;
  615.     int orig_column = current_column(), cur_column, new_indent;
  616.     int restore_point = 0;
  617.     int cmd, n;
  618.  
  619.     /* Keep track of how many consecutive tabs have been typed:    */
  620.     if ((prev_cmd==LISP_TAB_1) || (prev_cmd==LISP_TAB_2))
  621.         this_cmd = LISP_TAB_2;
  622.     else this_cmd = LISP_TAB_1;
  623.  
  624.  
  625.     if (lisp_tab_mode == 0)        /* Special case...        */
  626.      {
  627. stupid_tab:    /* Just treat as conventional tab...            */
  628.        point = orig;
  629.        insert('\t');
  630.        return;
  631.      }
  632.  
  633.     to_indentation();
  634.     cur_column = current_column();
  635.     if (orig_column>cur_column)
  636.      { /* Here if not in indentation...                */
  637.  
  638.        restore_point = 1;
  639.        switch(lisp_tab_mode & 3)
  640.          { case 0:
  641.                 goto stupid_tab;
  642.            case 1:
  643.                 goto simu_tab;
  644.            default:    break;
  645.          }
  646.      }
  647.  
  648.  
  649.     /* Extract appropriate 3-bit field from lisp_tab_mode to
  650.      * advise us in current situation:                */
  651.  
  652.     cmd = lisp_tab_mode >> 3;
  653.     switch (prev_cmd)
  654.      { case LISP_TAB_2:    cmd = cmd >> 3;
  655.        case LISP_TAB_1:    cmd = cmd >> 3;
  656.        default:        break;
  657.  
  658.      }
  659.  
  660.     /* Dispatch on command bits:                    */
  661.  
  662.      cmd = cmd & 7;
  663.      switch (cmd)
  664.         { case 0:    goto stupid_tab;
  665.           case 1:    new_indent = lisp_compute_indent();
  666.             goto set_indent;
  667.           case 2:    new_indent = lisp_tab_after(cur_column);
  668.             goto set_indent;
  669.  
  670.           case 3:    new_indent = lisp_tab_size;
  671.             goto set_indent;
  672.           case 4:    new_indent = 0;
  673.             goto set_indent;
  674.           default:
  675. simu_tab:
  676.           case 5:    point = orig;
  677.             cur_column = current_column();
  678.             n = lisp_tab_after(cur_column);
  679.             do (insert(' '));
  680.             while (current_column() < n);
  681.             return;
  682.         }
  683.  
  684. set_indent:
  685.     /* Set mark to point, allowing original position to be restored
  686.      * even if theres insertion/deletion.                */
  687.     mark = orig;
  688.  
  689.     to_column(new_indent);
  690. oldpos:    /* Restore previous position (modulo insertions/deletions), and
  691.      * return:                            */
  692.     if (restore_point) point=mark;
  693.  
  694. newpos:    return;
  695.  
  696.  }
  697.  
  698. command move_backward_thing() on lisp_tab[ALT('b')]
  699.  {    int it = backward_thing();
  700. /* debugging printout, no longer needed:
  701.     switch(it)
  702.      { case 2:    say("Moved up a level");    break;
  703.        case 1:    say("OK.");            break;
  704.        case 0:    say("Blocked");            break;
  705.        default:    say("It = %d", it);        break;
  706.      }
  707.  */
  708.  }
  709.  
  710. command lisp_comment() on lisp_tab[ALT(';')]
  711.  {    int *svmark = alloc_spot();
  712.     *svmark = mark;
  713.     end_of_line();
  714.     if (lisp_comment_col > (current_column() + 1)) 
  715.         to_column(lisp_comment_col);
  716.     else insert(' ');
  717.     insert(';');
  718.     insert(' ');
  719.     mark = *svmark;
  720.     free_spot(svmark);
  721.  }
  722.  
  723. /* Some old stuff:
  724.  */
  725.  
  726. command lisp_up_level() on lisp_tab[ALT(CTRL('u'))]
  727.  {    lisp_move_level(-1,RIGHTD,LEFTD);
  728.  }
  729.  
  730. command lisp_down_level() on lisp_tab[ALT(CTRL('d'))]
  731.  {    lisp_move_level(1,LEFTD,RIGHTD);  /* not quite right */
  732.  }
  733.  
  734. /* Enter LISP mode:
  735.  */
  736. command lisp_mode()
  737.  {
  738.     mode_keys = lisp_tab;        /* use these keys */
  739.     lisp_tab[')'] = (short) show_matching_delimiter;
  740.     lisp_tab[']'] = (short) show_matching_delimiter;
  741.     lisp_tab['}'] = (short) show_matching_delimiter;
  742.     lisp_tab[CTRL('H')] = (short) backward_delete_character; 
  743.     major_mode = strsave("Lisp");
  744.     make_mode();
  745.     indenter = lisp_indenter;
  746.     auto_indent = 1;
  747.     margin_right = 80;
  748.     delete_hacking_tabs = 1;
  749.  }
  750.  
  751. /* make this the default mode for .LSP (Lisp) and .S (Scheme) files     */
  752.  
  753. suffix_lsp()    { lisp_mode(); }
  754. suffix_s()    { lisp_mode(); }
  755. suffix_scm()    { lisp_mode(); }
  756.  
  757.  
  758. /*** End ***/
  759.